#pragma once

#include "Common.hpp"
#include "ShmPageCache.hpp"



class ShmCentralCache
{
public:
    // 获取单例实例的静态成员函数
    static ShmCentralCache* GetInstance() {
        // 使用双重检查锁定来确保线程安全
        if (_instance == nullptr) {
            std::lock_guard<std::mutex> lock(_mutex);
            if (_instance == nullptr) {
                _instance = new ShmCentralCache();
            }
        }
        return _instance;
    }



    // 从SpanList或者page cache获取一个span
    Span* GetOneSpan(SpanList& list, size_t size) {
        // 在ShmCentralCache中, 查看当前对应的桶中, 是否挂着Span
        Span* it = list.Begin();
        while (it != list.End()) {
            if (it->_freeList != nullptr)
                return it;

            it = it->_next;
        }

        // 没有空闲的Span, 只能从PageCache申请Page

        // 向PageCache申请时上Page全局的锁
        //_pageCache->GetPageMtx().lock();

        Span* span = _pageCache->NewSpan(SizeClass::NumMovePage(size));
        span->_objSize = size;

        //_pageCache->GetPageMtx().unlock();
        // 解除Page全局的锁
        // 此时不需要加上刚刚解除的桶锁,没有其他线程会访问到这个Span

        // 计算span的大块内存的起始地址和大块内存的大小
        char* start = reinterpret_cast<char*>(span->_headPtr);	// 当前页号起始地址
        char* end = start + (span->_n << MY_PAGE_SHIFT);

        int pos = span->_spanPos;    // 记录拆分小内存地址与所属span首地址的偏移量
        span->_freeList = nullptr;


        // 把大块内存切成自由链表挂到桶中
        while (start < end) {
            NextObj(start) = span->_freeList;
            ObjPos(start) = pos;
            ObjFile(start) = span->_fileId;

            span->_freeList = start;
            start += size;
            pos += size;
        }

        list.PushFront(span);	//交给CentralCache使用

        return span;
    }

    // 从中心缓存中获取所需大小内存
    void* FetchRangeObj(size_t size) {
        size_t alignSize = SizeClass::RoundUp(size);	// 获取对齐后的内存大小
        size_t index = SizeClass::Index(alignSize);     // 获取桶的位置
        void* start = nullptr;
        // 上锁
        // 在单个桶内操作,不影响其他桶,只需要在操作的那个桶里上锁就行了
        //_spanLists[index].GetSpanMtx().lock();

        //从对应桶中获取一个非空的span
        Span* span = GetOneSpan(_spanLists[index], alignSize);

        assert(span);
        assert(span->_freeList);

        // 从span获取一个对齐后的内存块
        start = span->_freeList;
        span->_freeList = NextObj(start);  // 更新空闲链表头
        span->_useCount++;                 // 更新使用计数

        //_spanLists[index].GetSpanMtx().unlock();
        // 解锁

        return start;
    }

    // 将一个对象释放到span跨度
    void ReleaseObjectToSpan(int32_t fileId, int32_t pos) {
        ShmServiceNode* shmNode = _shmManger->GetShmNodeByFileId(fileId);
        Span* span = reinterpret_cast<Span*>(shmNode->_pageMap[pos >> PAGE_SHIFT]);
        void* obj = reinterpret_cast<void*>(reinterpret_cast<char*>(shmNode->_shmSvr->GetShmHeader()) + pos);
        int index = SizeClass::Index(span->_objSize);

        // 将内存对象头插到CentralCache对应大小的桶对应的Span的_freeList

        NextObj(obj) = span->_freeList;
        ObjPos(obj) = pos;
        ObjFile(obj) = span->_fileId;

        span->_freeList = obj;
        span->_useCount--;

        // 当这个Span里切出去的内存对象全部回来后, 这个Span就可以还给PageCache
        if (span->_useCount == 0) {
            _spanLists[index].Erase(span);
            span->_freeList = nullptr;
            span->_prev = nullptr;
            span->_next = nullptr;

            // 将这个Span交给PageCache合并
            //_pageCache->GetPageMtx().lock(); // Page上锁
            _pageCache->ReleaseSpanToPageCache(span);
            //_pageCache->GetPageMtx().unlock(); // Page解锁
        }

        //_spanLists[index].GetSpanMtx().unlock();
    }


private:
    ShmCentralCache() {
        _pageCache = ShmPageCache::GetInstance();
        _shmManger = ShmServiceManger::GetInstance();
    }

    ~ShmCentralCache() {}


    ShmCentralCache(const ShmCentralCache&) = delete;
    ShmCentralCache& operator=(const ShmCentralCache&) = delete;

    static ShmCentralCache* _instance; // 静态成员变量，保存唯一实例的指针
    static std::mutex _mutex; // 静态互斥量，用于实现线程安全

private:
    SpanList _spanLists[NFREELIST];

    ShmPageCache* _pageCache = nullptr;
    ShmServiceManger* _shmManger = nullptr;

};

ShmCentralCache* ShmCentralCache::_instance = nullptr;
std::mutex ShmCentralCache::_mutex;